package com.walter.tencent_tts.core.tts;

import com.walter.tencent_tts.core.tts.util.Md5Util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * 语音合成类
 *
 * @author wuchenxi
 * @date 2019-05-24
 */
@Slf4j
@Component
public class Tts {

    @Value("${tencent.tts.url}")
    private String url;

    @Value("${tencent.tts.app_id}")
    private String appId;

    @Value("${tencent.tts.app_key}")
    private String appKey;

    @Value("${tencent.tts.nonce_str}")
    private String nonceStr;

    @Value("${tencent.tts.speaker}")
    private String speaker;

    @Value("${tencent.tts.format}")
    private String format;

    @Value("${tencent.tts.volume}")
    private String volume;

    @Value("${tencent.tts.speed}")
    private String speed;

    @Value("${tencent.tts.aht}")
    private String aht;

    @Value("${tencent.tts.apc}")
    private String apc;

    @Autowired
    private RestTemplate restTemplate;

    /**
     * @see <a href="https://blog.csdn.net/AndCo/article/details/80329538">将图片和音频转换成base64直接在HTML中使用</a>
     */
    private static final String CONTENT_TYPE = "data:audio/x-wav;base64,";

    /**
     * 获取语音合成路径
     *
     * @param text
     * @return
     */
    public String getAudio(String text) {
        final String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);

        Map<String, String> params = new HashMap<>(16);
        params.put("app_id", appId);
        params.put("time_stamp", timeStamp);
        params.put("nonce_str", nonceStr);
        params.put("speaker", speaker);
        params.put("format", format);
        params.put("volume", volume);
        params.put("speed", speed);
        params.put("aht", aht);
        params.put("apc", apc);
        params.put("text", text);

        this.getSign(params);

        return this.getAudioBase64(text, timeStamp, params);
    }

    private String getAudioBase64(String text, String timeStamp, Map<String, String> params) {
        String url = null;
        try {
            url = "https://api.ai.qq.com/fcgi-bin/aai/aai_tts?"
                    + "app_id=" + appId
                    + "&time_stamp=" + timeStamp
                    + "&nonce_str=" + nonceStr
                    + "&sign=" + params.get("sign")
                    + "&speaker=" + speaker
                    + "&format=" + format
                    + "&volume=" + volume
                    + "&speed=" + speed
                    + "&aht=" + aht
                    + "&apc=" + apc
                    + "&text=" + URLEncoder.encode(text, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error("URL 编码出错！", e);
        }
        ResponseEntity<Map> response = restTemplate.getForEntity(url, Map.class);

        this.checkResponse(response);

        String speech = this.getSpeech(response);
        return CONTENT_TYPE + speech;
    }

    /**
     * 获取语音 base64 字符串
     *
     * @param response
     * @return
     */
    @SuppressWarnings("all")
    private String getSpeech(ResponseEntity<Map> response) {
        Map<String, String> dataMap = (Map<String, String>) response.getBody().get("data");
        return dataMap.get("speech");
    }

    /**
     * 校验响应数据
     *
     * @param response
     */
    private void checkResponse(ResponseEntity<Map> response) {
        if (response == null || response.getBody() == null) {
            throw new RuntimeException("访问语音合成接口获取数据为空！");
        }

        Map body = response.getBody();
        int ret = Integer.parseInt(body.get("ret").toString());
        if (ret < 0) {
            throw new RuntimeException("系统错误！");
        }
        if (ret > 0) {
            log.error("业务错误！ret={}", ret);
            throw new RuntimeException("业务错误！");
        }
    }

    private void getSign(Map<String, String> map) {

        ArrayList<String> strings = new ArrayList<>(map.keySet());
        strings.sort(String::compareTo);

        Map<String, String> treeMap = new TreeMap<>();
        strings.forEach(s -> {
            if (s != null && !"".equals(s)) {
                treeMap.put(s, map.get(s));
            }
        });

        String reqSign = this.getReqSign(treeMap);

        map.put("sign", Md5Util.md5(reqSign).toUpperCase());
    }

    private String getReqSign(Map<String, String> map) {
        final StringBuilder sb = new StringBuilder();
        map.forEach((key, val) -> {
            try {
                if (val != null && !"".equals(val)) {
                    sb.append(key).append("=").append(URLEncoder.encode(val, "utf-8")).append("&");
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        });
        return sb.append("app_key=").append(appKey).toString();
    }
}
